/*
 * Eugen Stoian <stoian.e@gmail.com> 2010
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "FunTable.h"
#include "UnaryOpNode.h"
#include "BinaryOpNode.h"
#include "ValueNode.h"
#include <cmath>
static SmartPtr<ExpressionTreeNode> minusDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="-")
        return 0;
    if(!node->right())
        return 0;

    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> right = 0;
    root = new UnaryOpNode("-");
    right = node->right()->derive(varName);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> plusDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="+")
        return 0;
    if(!node->right())
        return 0;

    SmartPtr<ExpressionTreeNode> root = 0;
    root = node->right()->derive(varName);
    if(!root)
        return 0;

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> sinDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="sin")
        return 0;
    if(!node->right())
        return 0;

    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    right = node->right()->copy();
    root = new UnaryOpNode("cos");
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> cosDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="cos")
        return 0;
    if(!node->right())
        return 0;

    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    right = node->right()->copy();
    root = new UnaryOpNode("sin");
    root->setRight(right);

    right = root;
    root = new UnaryOpNode("-");
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> tanDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="tg")
        return 0;
    if(!node->right())
        return 0;

    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    right = node->right()->copy();
    root = new UnaryOpNode("cos");
    root->setRight(right);

    left = root;
    right = new ValueNode(Real(2.0));
    root = BinaryOpNode::makeNew("^");
    root->setLeft(left);
    root->setRight(right);

    right = root;
    left = new ValueNode(Real(1.0));
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> sinhDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="sh")
        return 0;
    if(!node->right())
        return 0;

    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    right = node->right()->copy();
    root = new UnaryOpNode("ch");
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}
static SmartPtr<ExpressionTreeNode> coshDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="ch")
        return 0;
    if(!node->right())
        return 0;

    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    right = node->right()->copy();
    root = new UnaryOpNode("sh");
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}
static SmartPtr<ExpressionTreeNode> tanhDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="tgh")
        return 0;
    if(!node->right())
        return 0;

    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    right = node->right()->copy();
    root = new UnaryOpNode("ch");
    root->setRight(right);

    left = root;
    right = new ValueNode(Real(2.0));
    root = BinaryOpNode::makeNew("^");
    root->setLeft(left);
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> asinDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="arcsin")
        return 0;
    if(!node->right())
        return 0;

    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    left = node->right()->copy();
    right = new ValueNode(2.0);
    root = BinaryOpNode::makeNew("^");
    root->setLeft(left);
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("-");
    root->setLeft(left);
    root->setRight(right);

    right  = root;
    root = new UnaryOpNode("radical");
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> acosDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="arccos")
        return 0;
    if(!node->right())
        return 0;
    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    left = node->right()->copy();
    right = new ValueNode(2.0);
    root = BinaryOpNode::makeNew("^");
    root->setLeft(left);
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("-");
    root->setLeft(left);
    root->setRight(right);

    right  = root;
    root = new UnaryOpNode("radical");
    root->setRight(right);

    right = root;
    left = new ValueNode(-1.0);
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> atanDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="arctg")
        return 0;
    if(!node->right())
        return 0;
    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    left = node->right()->copy();
    right = new ValueNode(2.0);
    root = BinaryOpNode::makeNew("^");
    root->setLeft(left);
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("+");
    root->setLeft(left);
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> asinhDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="arcsh")
        return 0;
    if(!node->right())
        return 0;
    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    left = node->right()->copy();
    right = new ValueNode(2.0);
    root = BinaryOpNode::makeNew("^");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("+");
    root->setLeft(left);
    root->setRight(right);

    right  = root;
    root = new UnaryOpNode("radical");
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> acoshDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="arcch")
        return 0;
    if(!node->right())
        return 0;
    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    left = node->right()->copy();
    right = new ValueNode(2.0);
    root = BinaryOpNode::makeNew("^");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("-");
    root->setLeft(left);
    root->setRight(right);

    right  = root;
    root = new UnaryOpNode("radical");
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> atanhDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="arctgh")
        return 0;
    if(!node->right())
        return 0;
    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    left = node->right()->copy();
    right = new ValueNode(2.0);
    root = BinaryOpNode::makeNew("^");
    root->setLeft(left);
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("-");
    root->setLeft(left);
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> logDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="ln")
        return 0;
    if(!node->right())
        return 0;
    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    left = new ValueNode(1.0);
    right = node->right()->copy();
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static SmartPtr<ExpressionTreeNode> sqrtDerivativeBuilder(SmartPtr<ExpressionTreeNode> node, const QString& varName) {

    if(!node)
        return 0;
    if(node->info()!="radical")
        return 0;
    if(!node->right())
        return 0;
    SmartPtr<ExpressionTreeNode> root = 0;
    SmartPtr<ExpressionTreeNode> left = 0;
    SmartPtr<ExpressionTreeNode> right = 0;

    left = new ValueNode(2.0);
    right = new UnaryOpNode("radical");
    right->setRight(node->right()->copy());
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    right = root;
    left = new ValueNode(1.0);
    root = BinaryOpNode::makeNew("/");
    root->setLeft(left);
    root->setRight(right);

    left = root;
    right = node->right()->derive(varName);
    if(!right)
        return 0;
    root = BinaryOpNode::makeNew("*");
    root->setLeft(left);
    root->setRight(right);

    SmartPtr<ExpressionTreeNode> tmp = root->simplified();
    return tmp;
}

static Real minus(const Real& arg) {
    return -arg;
}

static Real plus(const Real& arg) {
    return arg;
}

/**
  *\fn FunTable::FunTable()
  *\brief
  *
  */
FunTable::FunTable() {
    _funHash["-_1"] = FunInfo(&minus,&minusDerivativeBuilder);
    _funHash["+_1"] = FunInfo(&plus,&plusDerivativeBuilder);
    _funHash["sin_1"] = FunInfo(&r_sin, &sinDerivativeBuilder);
    _funHash["cos_1"] = FunInfo(&r_cos, &cosDerivativeBuilder);
    _funHash["tan_1"] = FunInfo(&r_tan, &tanDerivativeBuilder);
    _funHash["sinh_1"] = FunInfo(&r_sinh, &sinhDerivativeBuilder);
    _funHash["cosh_1"] = FunInfo(&r_cosh, &coshDerivativeBuilder);
    _funHash["tanh_1"] = FunInfo(&r_tanh, &tanhDerivativeBuilder);
    _funHash["asin_1"] = FunInfo(&r_asin, &asinDerivativeBuilder);
    _funHash["acos_1"] = FunInfo(&r_acos, &acosDerivativeBuilder);
    _funHash["atan_1"] = FunInfo(&r_atan, &atanDerivativeBuilder);
    _funHash["asinh_1"] = FunInfo(&r_asinh, &asinhDerivativeBuilder);
    _funHash["acosh_1"] = FunInfo(&r_acosh, &acoshDerivativeBuilder);
    _funHash["atanh_1"] = FunInfo(&r_atanh, &atanhDerivativeBuilder);
    _funHash["ln_1"] = FunInfo(&r_log, &logDerivativeBuilder);
    _funHash["sqrt_1"] = FunInfo(&r_sqrt, &sqrtDerivativeBuilder);
}

/**
  *\fn FunTable& FunTable::instance()
  *\brief
  *
  */
FunTable& FunTable::instance() {
    static FunTable * funTable = new FunTable();
    return *funTable;
}

/**
  *\fn void FunTable::addFun(const QString& signature, MathUnaryFun fun, ExpressionTreeOp derivativeBuilder)
  *\brief
  *
  */
void FunTable::addFun(const QString& signature, MathUnaryFun fun, ExpressionTreeOp derivativeBuilder) {
    instance()._funHash[signature] = FunInfo(fun,derivativeBuilder);
}

/**
  *\fn MathUnaryFun FunTable::fun(const QString& signature)
  *\brief
  *
  */
MathUnaryFun FunTable::fun(const QString& signature) {
    FunInfo fi = instance()._funHash.value(signature);
    return fi._fun;
}

/**
  *\fn ExpressionTreeOp FunTable::derivativeBuilder(const QString& signature)
  *\brief
  *
  */
ExpressionTreeOp FunTable::derivativeBuilder(const QString& signature) {
    FunInfo fi = instance()._funHash.value(signature);
    return fi._derivativeBuilder;
}

/**
  *\fn QStringList FunTable::names()
  *\brief
  *
  */
QStringList FunTable::names() {
    return instance()._funHash.keys();
}



bool FunTable::contains(const QString& signature){
    return instance()._funHash.keys().contains(signature);
}
